@@ -6,5 +6,7 @@ from photo import views as photo_views  | 
            ||
| 6 | 6 | 
                 | 
            
| 7 | 7 | 
                 | 
            
| 8 | 8 | 
                urlpatterns = [  | 
            
| 9 | 
                + url(r'^uuid_init$', photo_views.uuid_init, name='uuid_init'),  | 
            |
| 10 | 
                + url(r'^uuid$', photo_views.uuid, name='uuid'),  | 
            |
| 9 | 11 | 
                url(r'^photos/upload$', photo_views.upload_photo, name='upload_photo'),  | 
            
| 10 | 12 | 
                ]  | 
            
                @@ -0,0 +1,4 @@  | 
            ||
| 1 | 
                +1、照片上传 —— 401  | 
            |
| 2 | 
                + 4010 —— 参数错误  | 
            |
| 3 | 
                + 4011 —— 摄影师不存在  | 
            |
| 4 | 
                + 4012 —— 照片已存在  | 
            
                @@ -40,7 +40,7 @@ urlpatterns += [  | 
            ||
| 40 | 40 | 
                # Wire up our API using automatic URL routing.  | 
            
| 41 | 41 | 
                # Additionally, we include login URLs for the browsable API.  | 
            
| 42 | 42 | 
                urlpatterns += [  | 
            
| 43 | 
                - url(r'^api/', include(router.urls)),  | 
            |
| 43 | 
                + url(r'^apihome/', include(router.urls)),  | 
            |
| 44 | 44 | 
                     url(r'^api-auth/', include('rest_framework.urls', namespace='rest_framework'))
               | 
            
| 45 | 45 | 
                ]  | 
            
| 46 | 46 | 
                 | 
            
                @@ -26,6 +26,10 @@ server {
               | 
            ||
| 26 | 26 | 
                alias /home/paiai/work/pai2/collect_static; # your Django project's static files - amend as required  | 
            
| 27 | 27 | 
                }  | 
            
| 28 | 28 | 
                 | 
            
| 29 | 
                +    location /p/  {
               | 
            |
| 30 | 
                + alias /home/paiai/work/pai2/media/photo; # Photo  | 
            |
| 31 | 
                + }  | 
            |
| 32 | 
                +  | 
            |
| 29 | 33 | 
                # Finally, send all non-media requests to the Django server.  | 
            
| 30 | 34 | 
                     location / {
               | 
            
| 31 | 35 | 
                # uwsgi_pass pai2;  | 
            
                @@ -2,12 +2,18 @@  | 
            ||
| 2 | 2 | 
                 | 
            
| 3 | 3 | 
                from django.contrib import admin  | 
            
| 4 | 4 | 
                 | 
            
| 5 | 
                -from photo.models import PhotosInfo  | 
            |
| 5 | 
                +from photo.models import UUIDInfo, PhotosInfo  | 
            |
| 6 | 
                +  | 
            |
| 7 | 
                +  | 
            |
| 8 | 
                +class UUIDInfoAdmin(admin.ModelAdmin):  | 
            |
| 9 | 
                +    list_display = ('uuid', 'lensman_id', 'status', 'created_at', 'updated_at')
               | 
            |
| 10 | 
                +    list_filter = ('lensman_id', 'status')
               | 
            |
| 6 | 11 | 
                 | 
            
| 7 | 12 | 
                 | 
            
| 8 | 13 | 
                class PhotosInfoAdmin(admin.ModelAdmin):  | 
            
| 9 | 
                -    list_display = ('lensman_id', 'session_id', 'photo_id', 'photo_path', 'status', 'created_at', 'updated_at')
               | 
            |
| 14 | 
                +    list_display = ('lensman_id', 'session_id', 'photo_id', 'photo_name', 'photo_path', 'status', 'created_at', 'updated_at')
               | 
            |
| 10 | 15 | 
                     list_filter = ('lensman_id', 'status')
               | 
            
| 11 | 16 | 
                 | 
            
| 12 | 17 | 
                 | 
            
| 18 | 
                +admin.site.register(UUIDInfo, UUIDInfoAdmin)  | 
            |
| 13 | 19 | 
                admin.site.register(PhotosInfo, PhotosInfoAdmin)  | 
            
                @@ -0,0 +1,29 @@  | 
            ||
| 1 | 
                +# -*- coding: utf-8 -*-  | 
            |
| 2 | 
                +from __future__ import unicode_literals  | 
            |
| 3 | 
                +  | 
            |
| 4 | 
                +from django.db import models, migrations  | 
            |
| 5 | 
                +  | 
            |
| 6 | 
                +  | 
            |
| 7 | 
                +class Migration(migrations.Migration):  | 
            |
| 8 | 
                +  | 
            |
| 9 | 
                + dependencies = [  | 
            |
| 10 | 
                +        ('photo', '0002_auto_20151113_1419'),
               | 
            |
| 11 | 
                + ]  | 
            |
| 12 | 
                +  | 
            |
| 13 | 
                + operations = [  | 
            |
| 14 | 
                + migrations.CreateModel(  | 
            |
| 15 | 
                + name='UUIDInfo',  | 
            |
| 16 | 
                + fields=[  | 
            |
| 17 | 
                +                ('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)),
               | 
            |
| 18 | 
                +                ('status', models.BooleanField(default=True, help_text='\u72b6\u6001', verbose_name='status')),
               | 
            |
| 19 | 
                +                ('created_at', models.DateTimeField(help_text='\u521b\u5efa\u65f6\u95f4', verbose_name='created_at', auto_now_add=True)),
               | 
            |
| 20 | 
                +                ('updated_at', models.DateTimeField(help_text='\u66f4\u65b0\u65f6\u95f4', verbose_name='updated_at', auto_now=True)),
               | 
            |
| 21 | 
                +                ('uuid', models.CharField(null=True, max_length=22, blank=True, help_text='\u552f\u4e00\u6807\u8bc6', unique=True, verbose_name='uuid', db_index=True)),
               | 
            |
| 22 | 
                +                ('lensman_id', models.CharField(max_length=255, blank=True, help_text='\u6444\u5f71\u5e08\u552f\u4e00\u6807\u8bc6', null=True, verbose_name='lensman_id', db_index=True)),
               | 
            |
| 23 | 
                + ],  | 
            |
| 24 | 
                +            options={
               | 
            |
| 25 | 
                + 'verbose_name': 'uuidinfo',  | 
            |
| 26 | 
                + 'verbose_name_plural': 'uuidinfo',  | 
            |
| 27 | 
                + },  | 
            |
| 28 | 
                + ),  | 
            |
| 29 | 
                + ]  | 
            
                @@ -0,0 +1,19 @@  | 
            ||
| 1 | 
                +# -*- coding: utf-8 -*-  | 
            |
| 2 | 
                +from __future__ import unicode_literals  | 
            |
| 3 | 
                +  | 
            |
| 4 | 
                +from django.db import models, migrations  | 
            |
| 5 | 
                +  | 
            |
| 6 | 
                +  | 
            |
| 7 | 
                +class Migration(migrations.Migration):  | 
            |
| 8 | 
                +  | 
            |
| 9 | 
                + dependencies = [  | 
            |
| 10 | 
                +        ('photo', '0003_uuidinfo'),
               | 
            |
| 11 | 
                + ]  | 
            |
| 12 | 
                +  | 
            |
| 13 | 
                + operations = [  | 
            |
| 14 | 
                + migrations.AddField(  | 
            |
| 15 | 
                + model_name='photosinfo',  | 
            |
| 16 | 
                + name='photo_name',  | 
            |
| 17 | 
                + field=models.CharField(help_text='\u7167\u7247\u5b58\u653e\u540d\u79f0', max_length=255, null=True, verbose_name='photo_name', blank=True),  | 
            |
| 18 | 
                + ),  | 
            |
| 19 | 
                + ]  | 
            
                @@ -7,10 +7,32 @@ from django.utils.translation import ugettext_lazy as _  | 
            ||
| 7 | 7 | 
                from pai2.basemodels import CreateUpdateMixin  | 
            
| 8 | 8 | 
                 | 
            
| 9 | 9 | 
                 | 
            
| 10 | 
                +class UUIDInfo(CreateUpdateMixin):  | 
            |
| 11 | 
                + uuid = models.CharField(_(u'uuid'), max_length=22, blank=True, null=True, help_text=u'唯一标识', db_index=True, unique=True)  | 
            |
| 12 | 
                + lensman_id = models.CharField(_(u'lensman_id'), max_length=255, blank=True, null=True, help_text=u'摄影师唯一标识', db_index=True)  | 
            |
| 13 | 
                +  | 
            |
| 14 | 
                + class Meta:  | 
            |
| 15 | 
                +        verbose_name = _('uuidinfo')
               | 
            |
| 16 | 
                +        verbose_name_plural = _('uuidinfo')
               | 
            |
| 17 | 
                +  | 
            |
| 18 | 
                + def __unicode__(self):  | 
            |
| 19 | 
                +        return u'{0.pk}'.format(self)
               | 
            |
| 20 | 
                +  | 
            |
| 21 | 
                + def _data(self):  | 
            |
| 22 | 
                +        return {
               | 
            |
| 23 | 
                + 'pk': self.pk,  | 
            |
| 24 | 
                + 'uuid': self.uuid,  | 
            |
| 25 | 
                + 'lensman_id': self.lensman_id,  | 
            |
| 26 | 
                + }  | 
            |
| 27 | 
                +  | 
            |
| 28 | 
                + data = property(_data)  | 
            |
| 29 | 
                +  | 
            |
| 30 | 
                +  | 
            |
| 10 | 31 | 
                class PhotosInfo(CreateUpdateMixin):  | 
            
| 11 | 32 | 
                lensman_id = models.CharField(_(u'lensman_id'), max_length=255, blank=True, null=True, help_text=u'摄影师唯一标识', db_index=True)  | 
            
| 12 | 33 | 
                session_id = models.CharField(_(u'session_id'), max_length=255, blank=True, null=True, help_text=u'照片组唯一标识', db_index=True)  | 
            
| 13 | 34 | 
                photo_id = models.CharField(_(u'photo_id'), max_length=255, blank=True, null=True, help_text=u'照片唯一标识', db_index=True, unique=True)  | 
            
| 35 | 
                + photo_name = models.CharField(_(u'photo_name'), max_length=255, blank=True, null=True, help_text=u'照片存放名称')  | 
            |
| 14 | 36 | 
                photo_path = models.CharField(_(u'photo_path'), max_length=255, blank=True, null=True, help_text=u'照片存放路径')  | 
            
| 15 | 37 | 
                 | 
            
| 16 | 38 | 
                class Meta:  | 
            
                @@ -25,7 +47,8 @@ class PhotosInfo(CreateUpdateMixin):  | 
            ||
| 25 | 47 | 
                 | 
            
| 26 | 48 | 
                @property  | 
            
| 27 | 49 | 
                def photo_url(self):  | 
            
| 28 | 
                -        return u'{0}/media/{1}'.format(settings.DOMAIN, self.photo_path) if self.photo_path else ''
               | 
            |
| 50 | 
                +        # return u'{0}/media/{1}'.format(settings.DOMAIN, self.photo_path) if self.photo_path else ''
               | 
            |
| 51 | 
                +        return u'{0}/p/{1}'.format(settings.DOMAIN, self.photo_name) if self.photo_name else ''
               | 
            |
| 29 | 52 | 
                 | 
            
| 30 | 53 | 
                def _data(self):  | 
            
| 31 | 54 | 
                         return {
               | 
            
                @@ -1,17 +1,52 @@  | 
            ||
| 1 | 1 | 
                # -*- coding: utf-8 -*-  | 
            
| 2 | 2 | 
                 | 
            
| 3 | 3 | 
                from django.core.files.storage import default_storage  | 
            
| 4 | 
                +from django.db import transaction  | 
            |
| 4 | 5 | 
                from django.http import JsonResponse  | 
            
| 5 | 6 | 
                 | 
            
| 6 | 7 | 
                from rest_framework import viewsets  | 
            
| 7 | 8 | 
                 | 
            
| 8 | 9 | 
                from account.models import LensmanInfo  | 
            
| 9 | 
                -from photo.models import PhotosInfo  | 
            |
| 10 | 
                +from photo.models import UUIDInfo, PhotosInfo  | 
            |
| 10 | 11 | 
                from photo.serializers import PhotosInfoSerializer  | 
            
| 11 | 12 | 
                 | 
            
| 13 | 
                +from utils.uuid_utils import curtailUUID  | 
            |
| 14 | 
                +  | 
            |
| 12 | 15 | 
                import os  | 
            
| 13 | 16 | 
                 | 
            
| 14 | 17 | 
                 | 
            
| 18 | 
                +def uuid_init(request):  | 
            |
| 19 | 
                +    num = int(request.GET.get('num', 1000))
               | 
            |
| 20 | 
                +  | 
            |
| 21 | 
                + for i in xrange(num):  | 
            |
| 22 | 
                + UUIDInfo.objects.create(uuid=curtailUUID())  | 
            |
| 23 | 
                +  | 
            |
| 24 | 
                +    return JsonResponse({
               | 
            |
| 25 | 
                + 'status': 200,  | 
            |
| 26 | 
                + 'message': u'UUID 更新成功',  | 
            |
| 27 | 
                + 'data': '',  | 
            |
| 28 | 
                + })  | 
            |
| 29 | 
                +  | 
            |
| 30 | 
                +  | 
            |
| 31 | 
                +# curl -X POST -F lensman_id=123 -F num=100 http://xfoto.com.cn/api/uuid  | 
            |
| 32 | 
                +@transaction.atomic  | 
            |
| 33 | 
                +def uuid(request):  | 
            |
| 34 | 
                +    lensman_id = request.POST.get('lensman_id', '')
               | 
            |
| 35 | 
                +    num = int(request.POST.get('num', 100))
               | 
            |
| 36 | 
                +  | 
            |
| 37 | 
                + uuids = UUIDInfo.objects.select_for_update().filter(status=True)[:num]  | 
            |
| 38 | 
                + for uuid in uuids:  | 
            |
| 39 | 
                + uuid.lensman_id = lensman_id  | 
            |
| 40 | 
                + uuid.status = False  | 
            |
| 41 | 
                + uuid.save()  | 
            |
| 42 | 
                +  | 
            |
| 43 | 
                +    return JsonResponse({
               | 
            |
| 44 | 
                + 'status': 200,  | 
            |
| 45 | 
                + 'message': u'获取唯一标识成功',  | 
            |
| 46 | 
                + 'data': [uuid.uuid for uuid in uuids],  | 
            |
| 47 | 
                + })  | 
            |
| 48 | 
                +  | 
            |
| 49 | 
                +  | 
            |
| 15 | 50 | 
                # [How to do a PUT request with curl?](http://stackoverflow.com/questions/13782198/how-to-do-a-put-request-with-curl)  | 
            
| 16 | 51 | 
                # Unfortunately, the -T is no substitute for -X PUT if you want to specify parameters with -d or -F.  | 
            
| 17 | 52 | 
                # -T sends the content of a file via PUT. To achieve the GET after a redirect, add the parameter --location  | 
            
                @@ -23,17 +58,16 @@ import os  | 
            ||
| 23 | 58 | 
                # name with the symbol <. The difference between @ and < is then that @ makes a file get attached in the post as a file upload,  | 
            
| 24 | 59 | 
                # while the < makes a text field and just get the contents for that text field from a file.  | 
            
| 25 | 60 | 
                #  | 
            
| 26 | 
                -# curl -X POST -F lensman_id=123 -F session_id=456 -F photo_id=789 -F photo=@7056288a9ddf2db294cf50a943920989.jpg;filename=789 http://xfoto.com.cn/api/photos/upload  | 
            |
| 61 | 
                +# curl -X POST -F lensman_id=123 -F session_id=456 -F photo=@7056288a9ddf2db294cf50a943920989.jpg;filename=789 http://xfoto.com.cn/api/photos/upload  | 
            |
| 27 | 62 | 
                def upload_photo(request):  | 
            
| 28 | 63 | 
                     lensman_id = request.POST.get('lensman_id', '')
               | 
            
| 29 | 64 | 
                     session_id = request.POST.get('session_id', '')
               | 
            
| 30 | 
                -    photo_id = request.POST.get('photo_id', '')
               | 
            |
| 31 | 65 | 
                 | 
            
| 32 | 66 | 
                     photo = request.FILES.get('photo', '')
               | 
            
| 33 | 67 | 
                 | 
            
| 34 | 
                - if not (lensman_id and session_id and photo_id and photo):  | 
            |
| 68 | 
                + if not (lensman_id and session_id and photo):  | 
            |
| 35 | 69 | 
                         return JsonResponse({
               | 
            
| 36 | 
                - 'status': 400,  | 
            |
| 70 | 
                + 'status': 4010,  | 
            |
| 37 | 71 | 
                'message': u'参数错误',  | 
            
| 38 | 72 | 
                })  | 
            
| 39 | 73 | 
                 | 
            
                @@ -41,12 +75,16 @@ def upload_photo(request):  | 
            ||
| 41 | 75 | 
                LensmanInfo.objects.get(lensman_id=lensman_id)  | 
            
| 42 | 76 | 
                except LensmanInfo.DoesNotExist:  | 
            
| 43 | 77 | 
                         return JsonResponse({
               | 
            
| 44 | 
                - 'status': 400,  | 
            |
| 45 | 
                - 'message': u'参数错误',  | 
            |
| 78 | 
                + 'status': 4011,  | 
            |
| 79 | 
                + 'message': u'摄影师不存在',  | 
            |
| 46 | 80 | 
                })  | 
            
| 47 | 81 | 
                 | 
            
| 82 | 
                + photo_id = curtailUUID()  | 
            |
| 83 | 
                +  | 
            |
| 48 | 84 | 
                _, extension = os.path.splitext(photo.name)  | 
            
| 49 | 
                -    photo_path = 'photo/{0}/{1}/{2}{3}'.format(lensman_id, session_id, photo_id, extension)
               | 
            |
| 85 | 
                +    # photo_path = 'photo/{0}/{1}/{2}{3}'.format(lensman_id, session_id, photo_id, extension)
               | 
            |
| 86 | 
                +    photo_name = '{0}{1}'.format(photo_id, extension)
               | 
            |
| 87 | 
                +    photo_path = 'photo/{0}'.format(photo_name)
               | 
            |
| 50 | 88 | 
                 | 
            
| 51 | 89 | 
                if default_storage.exists(photo_path):  | 
            
| 52 | 90 | 
                default_storage.delete(photo_path)  | 
            
                @@ -56,6 +94,7 @@ def upload_photo(request):  | 
            ||
| 56 | 94 | 
                lensman_id=lensman_id,  | 
            
| 57 | 95 | 
                session_id=session_id,  | 
            
| 58 | 96 | 
                photo_id=photo_id,  | 
            
| 97 | 
                + photo_name=photo_name,  | 
            |
| 59 | 98 | 
                photo_path=photo_path  | 
            
| 60 | 99 | 
                )  | 
            
| 61 | 100 | 
                 | 
            
                @@ -9,4 +9,5 @@ ipython==4.0.0  | 
            ||
| 9 | 9 | 
                pep8==1.6.2  | 
            
| 10 | 10 | 
                pillow==2.9.0  | 
            
| 11 | 11 | 
                pytz==2015.7  | 
            
| 12 | 
                +shortuuid==0.4.2  | 
            |
| 12 | 13 | 
                uWSGI==2.0.11.1  | 
            
                @@ -0,0 +1,16 @@  | 
            ||
| 1 | 
                +# -*- coding: utf-8 -*-  | 
            |
| 2 | 
                +  | 
            |
| 3 | 
                +from photo.models import UUIDInfo  | 
            |
| 4 | 
                +  | 
            |
| 5 | 
                +import shortuuid  | 
            |
| 6 | 
                +  | 
            |
| 7 | 
                +  | 
            |
| 8 | 
                +def curtailUUID(length=10):  | 
            |
| 9 | 
                + flag = True  | 
            |
| 10 | 
                + while flag:  | 
            |
| 11 | 
                + uuid = shortuuid.uuid()[-length:]  | 
            |
| 12 | 
                + try:  | 
            |
| 13 | 
                + UUIDInfo.objects.get(uuid=uuid)  | 
            |
| 14 | 
                + except UUIDInfo.DoesNotExist:  | 
            |
| 15 | 
                + flag = False  | 
            |
| 16 | 
                + return uuid  |